define(['base', 'moment'], (App, moment) =>
  App.controller('BinaryFileController', function(
    $scope,
    $rootScope,
    $http,
    $timeout,
    $element,
    ide,
    waitFor
  ) {
    const MAX_FILE_SIZE = 2 * 1024 * 1024
    const MAX_URL_LENGTH = 60
    const FRONT_OF_URL_LENGTH = 35
    const FILLER = '...'
    const TAIL_OF_URL_LENGTH =
      MAX_URL_LENGTH - FRONT_OF_URL_LENGTH - FILLER.length

    const textExtensions = ['bib', 'tex', 'txt', 'cls', 'sty']
    const imageExtensions = ['png', 'jpg', 'jpeg', 'gif']
    const previewableExtensions = []

    const extension = file =>
      file.name
        .split('.')
        .pop()
        .toLowerCase()

    $scope.isTextFile = () =>
      textExtensions.indexOf(extension($scope.openFile)) > -1
    $scope.isImageFile = () =>
      imageExtensions.indexOf(extension($scope.openFile)) > -1
    $scope.isPreviewableFile = () =>
      previewableExtensions.indexOf(extension($scope.openFile)) > -1
    $scope.isUnpreviewableFile = () =>
      !$scope.isTextFile() &&
      !$scope.isImageFile() &&
      !$scope.isPreviewableFile()

    $scope.textPreview = {
      loading: false,
      shouldShowDots: false,
      error: false,
      data: null
    }

    $scope.refreshing = false
    $scope.refreshError = null

    $scope.displayUrl = function(url) {
      if (url == null) {
        return
      }
      if (url.length > MAX_URL_LENGTH) {
        const front = url.slice(0, FRONT_OF_URL_LENGTH)
        const tail = url.slice(url.length - TAIL_OF_URL_LENGTH)
        return front + FILLER + tail
      }
      return url
    }

    $scope.refreshFile = function(file) {
      $scope.refreshing = true
      $scope.refreshError = null
      ide.fileTreeManager
        .refreshLinkedFile(file)
        .then(function(response) {
          const { data } = response
          const newFileId = data.new_file_id
          $timeout(
            () =>
              waitFor(() => ide.fileTreeManager.findEntityById(newFileId), 5000)
                .then(newFile => ide.binaryFilesManager.openFile(newFile))
                .catch(err => console.warn(err)),

            0
          )
          $scope.refreshError = null
        })
        .catch(response => ($scope.refreshError = response.data))
        .finally(() => {
          $scope.refreshing = false
          const provider = file.linkedFileData.provider
          if (
            provider === 'mendeley' ||
            provider === 'zotero' ||
            file.name.match(/^.*\.bib$/)
          ) {
            ide.$scope.$emit('references:should-reindex', {})
          }
        })
    }

    // Callback fired when the `img` tag fails to load,
    // `failedLoad` used to show the "No Preview" message
    $scope.failedLoad = false
    window.sl_binaryFilePreviewError = () => {
      $scope.failedLoad = true
      $scope.$apply()
    }

    // Callback fired when the `img` tag is done loading,
    // `imgLoaded` is used to show the spinner gif while loading
    $scope.imgLoaded = false
    window.sl_binaryFilePreviewLoaded = () => {
      $scope.imgLoaded = true
      $scope.$apply()
    }

    if ($scope.isTextFile()) {
      loadTextFilePreview()
    }

    function loadTextFilePreview() {
      const url = `/project/${window.project_id}/file/${$scope.openFile.id}`
      let truncated = false
      displayPreviewLoading()
      getFileSize(url)
        .then(fileSize => {
          const opts = {}
          if (fileSize > MAX_FILE_SIZE) {
            truncated = true
            opts.maxSize = MAX_FILE_SIZE
          }
          return getFileContents(url, opts)
        })
        .then(contents => {
          const displayedContents = truncated
            ? truncateFileContents(contents)
            : contents
          displayPreview(displayedContents, truncated)
        })
        .catch(err => {
          console.error(err)
          displayPreviewError()
        })
    }

    function getFileSize(url) {
      return $http.head(url).then(response => {
        const size = parseInt(response.headers('Content-Length'), 10)
        if (isNaN(size)) {
          throw new Error('Could not parse Content-Length header')
        }
        return size
      })
    }

    function getFileContents(url, opts = {}) {
      const { maxSize } = opts
      if (maxSize != null) {
        url += `?range=0-${maxSize}`
      }
      return $http
        .get(url, {
          transformResponse: null // Don't parse JSON
        })
        .then(response => {
          return response.data
        })
    }

    function truncateFileContents(contents) {
      return contents.replace(/\n.*$/, '')
    }

    function displayPreviewLoading() {
      $scope.textPreview.data = null
      $scope.textPreview.loading = true
      $scope.textPreview.shouldShowDots = false
      $scope.$apply()
    }

    function displayPreview(contents, truncated) {
      $scope.textPreview.error = false
      $scope.textPreview.data = contents
      $scope.textPreview.shouldShowDots = truncated
      $timeout(setPreviewHeight, 0)
    }

    function displayPreviewError() {
      $scope.textPreview.error = true
      $scope.textPreview.loading = false
    }

    function setPreviewHeight() {
      const $preview = $element.find('.text-preview .scroll-container')
      const $header = $element.find('.binary-file-header')
      const maxHeight = $element.height() - $header.height() - 14 // borders + margin
      $preview.css({ 'max-height': maxHeight })
      // Don't show the preview until we've set the height, otherwise we jump around
      $scope.textPreview.loading = false
    }
  }))
